package org.tlang.ast.list.clazz;

import org.tlang.ast.AST;
import org.tlang.ast.ASTLeaf;
import org.tlang.ast.ASTList;
import org.tlang.context.Location;
import org.tlang.context.SymbolTable;
import org.tlang.context.TypeTable;
import org.tlang.context.ValueTable;
import org.tlang.exception.EvalException;
import org.tlang.exception.TypeException;
import org.tlang.keywords.KeyWords;
import org.tlang.metaclass.TVar;
import org.tlang.type.ClassType;
import org.tlang.type.Type;

import java.util.List;

/**
 * class_define: "class" IDENTIFIER [ "extends" IDENTIFIER ] class_body
 */
public class ClassDefine extends ASTList {
    public static final int INDEX_THIS_IN_FIELD_SYMBOLS = 0;
    private int indexSuperInGlobal;
    private int indexSelfInGlobal;
    private ValueTable methodValueTable;
    private ValueTable fieldValueTable;
    private ClassType selfType;

    public ClassDefine(List<AST> children) {
        super(children);
        indexSuperInGlobal = Location.UNKNOWN;
        indexSelfInGlobal = Location.UNKNOWN;
    }

    public String name() {
        return ((ASTLeaf) child(0)).token().getText();
    }

    private String superName() {
        if (numChildren() < 3) {
            return null;
        } else {
            return ((ASTLeaf) child(1)).token().getText();
        }
    }

    private ClassBody classBody() {
        return (ClassBody) child(numChildren() - 1);
    }

    @Override
    public String toString() {
        return "(class " + name() + " extends " + superName() + " " + classBody() + ")";
    }

    @Override
    public void lookup(SymbolTable globalSymbolTable, TypeTable globalTypeTable) {
        // self index  in global
        indexSelfInGlobal = globalSymbolTable.add(name());

        // super index in global
        if (superName() != null) {
            Location location = globalSymbolTable.where(superName());
            if (location == null) {
                throw new EvalException("super class <" + superName() + "> not defined");
            } else {
                indexSuperInGlobal = location.index();
            }
        }

        // 符号表初始化
        SymbolTable methodSymbolTable = new SymbolTable(globalSymbolTable, "class<" + name() + ">, method");
        SymbolTable fieldSymbolTable = new SymbolTable(methodSymbolTable, "class<" + name() + ">, field");
        fieldSymbolTable.add(KeyWords.THIS); // this永远位于符号表的首位

        // 类型表初始化
        TypeTable methodTypeTable = new TypeTable(methodSymbolTable.size(), globalTypeTable, "class<" + name() + ">, method");
        TypeTable fieldTypeTable = new TypeTable(fieldSymbolTable.size(), methodTypeTable, "class<" + name() + ">, field");

        // 构造自己的类型并初始化
        selfType = new ClassType(name(), indexSelfInGlobal, indexSuperInGlobal, fieldSymbolTable, fieldTypeTable);
        fieldTypeTable.put(INDEX_THIS_IN_FIELD_SYMBOLS, selfType);

        // 在全局类型表中注册自己的类型
        globalTypeTable.put(indexSelfInGlobal, selfType);

        if (indexSuperInGlobal != Location.UNKNOWN) { // 是否有继承的父类
            // 拷贝父类的符号表
            ClassType superClassType = (ClassType) globalTypeTable.get(0, indexSuperInGlobal);
            fieldSymbolTable.append(superClassType.fieldSymbolTable());
            methodSymbolTable.append(superClassType.methodSymbolTable());

            // 拷贝父类的类型表
            for (int i = 1; i < fieldSymbolTable.size(); i++) {
                fieldTypeTable.put(i, superClassType.fieldTypeTable().get(0, i));
            }
            for (int i = 0; i < methodSymbolTable.size(); i++) {
                methodTypeTable.put(i, superClassType.methodTypeTable().get(0, i));
            }
        }

        // 查找自身的field和method符号
        classBody().lookup(fieldSymbolTable, fieldTypeTable);
    }

    @Override
    public Type typeCheck(TypeTable globalTypeTable) throws TypeException {
        // 检查父类是否有效
        if (indexSuperInGlobal != Location.UNKNOWN) {
            Type type = globalTypeTable.get(0, indexSuperInGlobal);
            if (!(type instanceof ClassType)) {
                throw new TypeException("super is not class type: " + superName(), this);
            }
        }

        classBody().typeCheck(selfType.fieldTypeTable());

        return selfType;
    }

    @Override
    public Object eval(ValueTable valueTable) {
        valueTable.add(indexSelfInGlobal, this);

        this.methodValueTable = new ValueTable(selfType.methodSymbolTable().size(), valueTable, "class<" + name() + ">, method");
        this.fieldValueTable = createValueTable("class<" + name() + ">, field");

        // 将成员变量和方法定义注入字段的valueTable中，因为嵌套关系，方法定义会自动注入到方法的valueTable中
        if (indexSuperInGlobal != Location.UNKNOWN) {
            ClassDefine superClass = (ClassDefine) valueTable.get(0, indexSuperInGlobal);
            superClass.classBody().eval(fieldValueTable);
        }
        this.classBody().eval(fieldValueTable);

        return name();
    }

    public ValueTable createValueTable(String tag) {
        return new ValueTable(selfType.fieldSymbolTable().size(), this.methodValueTable, tag);
    }

    public void initTObject(ValueTable valueTable) {
        for (int i = 1; i < selfType.fieldSymbolTable().size(); i++) {
            Object obj = this.fieldValueTable.get(0, i);
            if (obj instanceof TVar) {
                valueTable.add(i, ((TVar) obj).copy());
            }
        }
    }

    public ValueTable methodValuetable() {
        return methodValueTable;
    }

    public ClassType classType() {
        return selfType;
    }
}
